import pygame, sys, os, random
from pygame.locals import *
from datamap import *
from level import *
from ai import GameAI
from driver import KeySignal
from gamesprite import *
from tools import *

class Panel(object):
    """ a draw and keyprocess container """
    LOCKTIME = 25 # to avert keyboard port delay
    def __init__(self, con, sub, layer = 0, focus = False, grid = 40):
        super(Panel, self).__init__()
        self.layer = layer
        self.focus = focus
        self.con = con
        self.sub = sub # subsurface
        self.grid = grid
        self.keybond = {}
        self.keylock = 0
        
    def regsiter_key(self, keys, func):
        for key in keys:
            self.keybond[key] = func
    
    def unregsiter_key(self, keys):
        for key in keys:
            if self.keybond.has_key(key):
                del self.keybond[key]

    def keyboard(self):
        if self.keylock < Panel.LOCKTIME:
            self.keylock += 1
        else:
            key = pygame.key.get_pressed()
            for keyname in self.keybond.keys():
                if key[keyname]:
                    self.keybond[keyname](keyname)

    def center_node(self, node, nsize, size):
        nodepos = (node[0] * self.grid, node[1] * self.grid)
        offpos = ((self.grid * nsize[0] - size[0]) / 2,
                  (self.grid * nsize[1] - size[1]) / 2)
        pos = (nodepos[0] + offpos[0], nodepos[1] + offpos[1])
        return pos

    def node_line(self, color):
        re = self.sub.get_rect()
        for x in xrange(self.grid, re.width, self.grid):
            pygame.draw.line(self.sub, color, (x, 0), (x, re.height))
        for y in xrange(self.grid, re.height, self.grid):
            pygame.draw.line(self.sub, color, (0, y), (re.width, y))

    def update(self):
        pass
    
class StartMenu(Panel):
    """ start program menu """
    KEYS = (K_UP, K_DOWN, K_RIGHT, K_LEFT, K_RETURN, K_ESCAPE)
    MENUSTR = ('Start Game', 'Choose Level', 'Quit Game')
    MENUSIZE = (400, 100)
    LVLSIZE = (200, 200)
    def __init__(self, con, sub):
        super(StartMenu, self).__init__(con, sub, -1, True)
        self.regsiter_key(StartMenu.KEYS, self.pros_keys)
        self.__bg = load_image('res\\startmenu.bmp')
        # init scroll menu
        subre = self.sub.get_rect()
        menupos = ((subre.width - StartMenu.MENUSIZE[0]) // 2, 350)
        menusize = (StartMenu.MENUSIZE[0], subre.bottom - menupos[1]) 
        self.__menu = ScrollMenu(self.con, 
                                 self.sub.subsurface(pygame.Rect(menupos, menusize)), 
                                 self.__init_menu(), 1)
        lvlpos = ((subre.width - StartMenu.LVLSIZE[0] * 2.125) // 2, 300)
        lvlsize = (StartMenu.LVLSIZE[0] * 2.125, subre.bottom - lvlpos[1]) 
        self.__lvl = ScrollMenu(self.con, 
                                 self.sub.subsurface(pygame.Rect(lvlpos, lvlsize)), 
                                 self.__init_level(), 2)
        self.__active = self.__menu
        self.con.register(self.__active)
        
    def __init_menu(self):
        frame = []
        font = pygame.font.Font('res\\comic.ttf', 50)
        font.set_bold(1)
        for i in xrange(len(StartMenu.MENUSTR)):
            frame.append(pygame.Surface(StartMenu.MENUSIZE))
            frame[i].set_colorkey((0, 0, 0))
            sf = font.render(StartMenu.MENUSTR[i], 1, (100, 200, 100))
            r = sf.get_rect()
            frame[i].blit(sf, ((StartMenu.MENUSIZE[0] - r.width) // 2, 
                               (StartMenu.MENUSIZE[1] - r.height) //2))
        return frame
    
    def __init_level(self):
        frame = []
        for level in xrange(0, len(mapcfg)):
            frame.append(mini_level(level, StartMenu.LVLSIZE))
        return frame
        
    def pros_keys(self, keyname):
        if keyname == K_UP or keyname == K_LEFT: self.__active.previous()
        elif keyname == K_DOWN or keyname == K_RIGHT: self.__active.next()
        elif keyname == K_RETURN: 
            sel = self.__active.current()
            if self.__active == self.__menu:
                if sel == 0: # start
                    self.con.unregister(self)
                    self.con.unregister(self.__active)
                    self.con.level_begin(0)
                elif sel == 1: # level
                    self.con.unregister(self.__menu)
                    self.con.register(self.__lvl)
                    self.__active = self.__lvl
                    self.keylock = 0
                elif sel == 2: # quit
                    sys.exit()
            elif self.__active == self.__lvl:
                self.con.unregister(self)
                self.con.unregister(self.__active)
                self.con.level_begin(sel)
        elif keyname == K_ESCAPE:
            if self.__active == self.__lvl:
                self.con.unregister(self.__lvl)
                self.con.register(self.__menu)
                self.__active = self.__menu
    
    def update(self):
        super(StartMenu, self).update()
        self.sub.blit(self.__bg, (0, 0))
        
class GameMenu(Panel):
    """ game pause menu """
    KEYS = (K_UP, K_DOWN, K_RIGHT, K_LEFT, K_RETURN, K_ESCAPE)
    MENUSTR = ('Continue', 'Restart', 'Mainmenu')
    MENUSIZE = (450, 160)
    LAYER = 1
    def __init__(self, con, sub, level, game):
        super(GameMenu, self).__init__(con, sub, layer = GameMenu.LAYER, focus = True)
        self.__game = game
        self.__level = level
        self.__bg = self.__init_bg()
        self.__re = pygame.Surface((80, 80))
        self.__re.fill((63, 165, 231))
        self.__re.set_alpha(0)
        self.__cgrect = []
        for x in xrange(10): # init reference map
            self.__cgrect.append([])
            for y in xrange(8):
                self.__cgrect[x].append(None)
        self.regsiter_key(GameMenu.KEYS, self.pros_keys)
        subre = self.sub.get_rect()
        menupos = ((subre.width - GameMenu.MENUSIZE[0] * 1.75) // 2, \
                   (subre.height - GameMenu.MENUSIZE[1] * 2.5) // 2)
        menusize = (GameMenu.MENUSIZE[0] * 1.75, GameMenu.MENUSIZE[1] * 2.5)
        self.__menu = ScrollMenu(self.con, 
                                 self.sub.subsurface(pygame.Rect(menupos, menusize)), 
                                 self.__init_menu(), 3, 0.5)
        self.con.register(self.__menu)
    
    def __init_bg(self):
        bg = pygame.Surface(self.sub.get_rect().size)
        bg.fill((0, 0, 0))
        """
        for x in xrange(80, 800, 80):
            pygame.draw.line(bg, (63, 165, 231), (x, 0), (x, 640))
        for y in xrange(80, 640, 80):
            pygame.draw.line(bg, (63, 165, 231), (0, y), (800, y))
        """
        bg.set_alpha(150)
        return bg
    
    def __init_menu(self):
        frame = []
        font = pygame.font.Font('res\\comic.ttf', 80)
        font.set_bold(1)
        for i in xrange(len(GameMenu.MENUSTR)):
            frame.append(pygame.Surface(GameMenu.MENUSIZE))
            frame[i].set_colorkey((0, 0, 0))
            sf = font.render(GameMenu.MENUSTR[i], 1, (100, 200, 100))
            r = sf.get_rect()
            frame[i].blit(sf, ((GameMenu.MENUSIZE[0] - r.width) // 2, 
                               (GameMenu.MENUSIZE[1] - r.height) //2))
        return frame

    def pros_keys(self, keyname):
        if keyname == K_UP or keyname == K_LEFT: self.__menu.previous()
        elif keyname == K_DOWN or keyname == K_RIGHT: self.__menu.next()
        elif keyname == K_RETURN:
            sel = self.__menu.current()
            self.con.unregister(self.__menu)
            self.con.unregister(self)
            if sel == 0: # resume
                self.__game.pause(False)
            elif sel == 1: # restart
                self.con.level_begin(self.__level)
                self.con.unregister(self.__game)
            elif sel == 2: # return
                self.con.start_menu()
                self.con.unregister(self.__game)

    def rect_cg(self):
        for x in xrange(10):
            for y in xrange(8):
                item = self.__cgrect[x][y]
                if item:
                    self.sub.blit(item[0], (x * 80, y * 80))
                    a = item[0].get_alpha() + item[1]
                    item[0].set_alpha(a)
                    if a > 250: item[1] = -item[1]
                    elif a < 0: self.__cgrect[x][y] = None

    def rect_random(self):
        x = random.randint(0, 9)
        y = random.randint(0, 7)
        if not self.__cgrect[x][y]:
            self.__cgrect[x][y] = [self.__re.copy(), 5]

    def update(self):
        super(GameMenu, self).update()
        #if random.randint(0, 100) < 100: 
        self.rect_random()
        self.rect_cg()
        self.sub.blit(self.__bg, (0, 0))

class ScrollItem(object):
    """ use in scroll menu """
    def __init__(self, size, alpha, scale, step, trans):
        super(ScrollItem, self).__init__()
        self.__wh = size
        self.__step = step
        if trans == 1: self.__init_vertikal(size, alpha, scale, step)
        elif trans == 2: self.__init_horizontal(size, alpha, scale, step)
        elif trans == 3: self.__init_diagonal(size, alpha, scale, step)
        
    def __init_vertikal(self, size, alpha, scale, step):
        self.__pos = (0, size[1] * scale)
        # move step
        self.__x = (size[0] * (1 - scale) / 2 / step, # up
                    size[0] * (1 - scale) / 2 / step # down
                    )
        self.__y = (-size[1] * scale / step, # up
                    size[1] / step, # down
                    )
        self.__size = (size[0] * (1 - scale) / step, size[1] * (1 - scale) / step)
        self.__alpha = (255 - alpha) / step
        # fade step
        self.__fx = (size[0] * scale * (1 - scale) / 2 / step, # up
                     size[0] * scale * (1 - scale) / 2 / step # down
                     )
        self.__fy = ((size[1] * scale) / step, # up
                     -(size[1] * scale * scale) / step, # down
                    )
        self.__fsize = (size[0] * scale * (1 - scale) / step, 
                        size[1] * scale * (1 - scale) / step)
        self.__falpha = alpha / step

    def __init_horizontal(self, size, alpha, scale, step):
        self.__pos = (size[0] * scale * 3 / 4, size[1] * scale * scale)
        # move step
        self.__x = (-size[0] * scale * 3 / 4 / step, # up
                    size[0] * (1 - scale / 4) / step # down
                    )
        self.__y = (-size[1] * scale / 4 / step, # up
                    -size[1] * scale / 4 / step, # down
                    )
        self.__size = (size[0] * (1 - scale) / step, size[1] * (1 - scale) / step)
        self.__alpha = (255 - alpha) / step
        # fade step
        self.__fx = (size[0] * scale * (1 - scale / 4) / step, # up
                     -size[0] * scale * scale / 4 / step # down
                     )
        self.__fy = (-size[1] * scale * scale / 4 / step, # up
                     -size[1] * scale * scale / 4 / step, # down
                    )
        self.__fsize = (size[0] * scale * (1 - scale) / step, 
                        size[1] * scale * (1 - scale) / step)
        self.__falpha = alpha / step

    def __init_diagonal(self, size, alpha, scale, step):
        self.__pos = (size[0] * scale * (1 + scale) / 2, size[1] * scale * (1 + scale))
        # move step
        self.__x = (size[0] * (1 - scale / 2) / step, # up
                    -size[0] * scale / 2 / step # down
                    )
        self.__y = (-size[1] * scale / step, # up
                    size[1] / step, # down
                    )
        self.__size = (size[0] * (1 - scale) / step, size[1] * (1 - scale) / step)
        self.__alpha = (255 - alpha) / step
        # fade step
        self.__fx = (size[0] * scale * (1 - scale / 2) / step, # up
                     -size[0] * scale * scale / 2 / step # down
                     )
        self.__fy = (-size[1] * scale * scale / step, # up
                     size[1] * scale / step, # down
                    )
        self.__fsize = (size[0] * scale * (1 - scale) / step, 
                        size[1] * scale * (1 - scale) / step)
        self.__falpha = alpha / step

    def normal(self, img):
        return img, self.__pos
        
    def move_up(self, step, img):
        pos = (self.__pos[0] + self.__x[0] * step, self.__pos[1] + self.__y[0] * step)
        image = pygame.transform.smoothscale(img, (int(self.__wh[0] - self.__size[0] * step), 
                                                   int(self.__wh[1] - self.__size[1] * step)))
        image.set_alpha(255 - self.__alpha * step)
        return image, pos
    
    def move_down(self, step, img):
        pos = (self.__pos[0] + self.__x[1] * step, self.__pos[1] + self.__y[1] * step)
        image = pygame.transform.smoothscale(img, (int(self.__wh[0] - self.__size[0] * step), 
                                                   int(self.__wh[1] - self.__size[1] * step)))
        image.set_alpha(255 - self.__alpha * step)
        return image, pos
    
    def fade_up(self, step, img):
        image, pos = self.move_up(self.__step, img)
        re = image.get_rect()
        pos = (pos[0] + self.__fx[0] * step, pos[1] + self.__fy[0] * step)
        image = pygame.transform.smoothscale(image, (int(re.width - self.__fsize[0] * step), 
                                                     int(re.height - self.__fsize[1] * step)))
        image.set_alpha(self.__falpha * (20 - step))
        return image, pos
    
    def fade_down(self, step, img):
        image, pos = self.move_down(self.__step, img)
        re = image.get_rect()
        pos = (pos[0] + self.__fx[1] * step, pos[1] + self.__fy[1] * step)
        image = pygame.transform.smoothscale(image, (int(re.width - self.__fsize[0] * step), 
                                                     int(re.height - self.__fsize[1] * step)))
        image.set_alpha(self.__falpha * (20 - step))
        return image, pos

class ScrollMenu(Panel):
    """ can scroll menu """
    STEP = 20
    SCALE = 0.75
    ALPHA = 80
    LAYER = 2
    def __init__(self, con, sub, frame, trans, scale = 0.75):
        super(ScrollMenu, self).__init__(con, sub, layer = ScrollMenu.LAYER)
        self.__frame = frame
        self.__index = 0
        self.__item = ScrollItem(
                                 self.__frame[0].get_rect().size,
                                 ScrollMenu.ALPHA,
                                 scale,
                                 ScrollMenu.STEP,
                                 trans
                                 )
        self.__tick = 0
        
    def next(self):
        if self.__tick == 0:
            playsound('menu')
            self.__index = (self.__index + 1) % len(self.__frame)
            self.__tick = -ScrollMenu.STEP
    
    def previous(self):
        if self.__tick == 0:
            playsound('menu')
            self.__index = (self.__index - 1) % len(self.__frame)
            self.__tick = ScrollMenu.STEP
    
    def current(self):
        return self.__index
    
    def __fadenex(self):
        return (self.__index + 2) % len(self.__frame)
    
    def __fadepre(self):
        return (self.__index - 2) % len(self.__frame)
    
    def __next(self):
        return (self.__index + 1) % len(self.__frame)
    
    def __previous(self):
        return (self.__index - 1) % len(self.__frame)
    
    def update(self):
        super(ScrollMenu, self).update()
        if self.__tick > 0: # previous
            fpre, fprepos = self.__item.fade_up(self.__tick, self.__frame[self.__previous()])
            fnex, fnexpos = self.__item.fade_down(20 - self.__tick, self.__frame[self.__fadenex()])
            nex, nexpos = self.__item.move_down(20 - self.__tick, self.__frame[self.__next()])
            cur, curpos = self.__item.move_up(self.__tick, self.__frame[self.__index])
            self.sub.blit(fpre, fprepos)
            self.sub.blit(fnex, fnexpos)
            self.sub.blit(nex, nexpos)
            self.sub.blit(cur, curpos)
            self.__tick -= 1
        elif self.__tick < 0: # next
            fpre, fprepos = self.__item.fade_up(20 + self.__tick, self.__frame[self.__fadepre()])
            fnex, fnexpos = self.__item.fade_down(-self.__tick, self.__frame[self.__next()])
            pre, prepos = self.__item.move_up(20 + self.__tick, self.__frame[self.__previous()])
            cur, curpos = self.__item.move_down(-self.__tick, self.__frame[self.__index])
            self.sub.blit(fpre, fprepos)
            self.sub.blit(fnex, fnexpos)
            self.sub.blit(pre, prepos)
            self.sub.blit(cur, curpos)
            self.__tick += 1
        else:
            pre, prepos = self.__item.move_up(ScrollMenu.STEP, self.__frame[self.__previous()])
            nex, nexpos = self.__item.move_down(ScrollMenu.STEP, self.__frame[self.__next()])
            cur, curpos = self.__item.normal(self.__frame[self.__index])
            self.sub.blit(pre, prepos)
            self.sub.blit(nex, nexpos)
            self.sub.blit(cur, curpos)

class GamePanel(Panel):
    """ game panel """
    KEYMAP = {
              K_w: K_UP,
              K_a: K_LEFT, 
              K_s: K_DOWN, 
              K_d: K_RIGHT,
              K_RCTRL: K_SPACE,
              K_UP: K_UP,
              K_DOWN: K_DOWN,
              K_LEFT: K_LEFT,
              K_RIGHT: K_RIGHT, 
              K_SPACE: K_SPACE,
              }
    GAMEKEYS1 = (K_w, K_a, K_s, K_d, K_SPACE)
    GAMEKEYS2 = (K_UP, K_DOWN, K_LEFT, K_RIGHT, K_RCTRL)
    FUNCKEYS = (K_ESCAPE, )
    GAMING = 0
    WIN = 1
    LOSE = 2
    def __init__(self, con, sub, level):
        super(GamePanel, self).__init__(con, sub, focus = True)
        self.regsiter_key(GamePanel.GAMEKEYS1, self.pros_keys1)
        self.regsiter_key(GamePanel.GAMEKEYS2, self.pros_keys2)
        self.regsiter_key(GamePanel.FUNCKEYS, self.pros_funckeys)
        self.__pause = False
        self.__level = level
        self.__lvlmap = DataMap(mapcfg[level])
        subre = pygame.Rect(DataMap.XAXIS * DataMap.GRID, 0, 
                            self.sub.get_rect().width - DataMap.XAXIS * DataMap.GRID, 
                            DataMap.YAXIS * DataMap.GRID)
        self.__state = StatePanel(self.con, self.sub.subsurface(subre), level, self.__lvlmap)
    
    def pros_funckeys(self, keyname):
        if not self.__pause:
            if keyname == K_ESCAPE:
                self.con.register(GameMenu(self.con, self.sub.get_parent(), self.__level, self))
                self.keylock = 0
                self.pause(True)

    def pros_keys1(self, keyname):
        if not self.__pause:
            for spr in self.__lvlmap.sprgroup.sprites((Player, )):
                spr.driver.send(KeySignal(GamePanel.KEYMAP[keyname]))
            
    def pros_keys2(self, keyname):
        if not self.__pause:
            for spr in self.__lvlmap.sprgroup.sprites((Plane, )):
                spr.driver.send(KeySignal(GamePanel.KEYMAP[keyname]))    
    
    def pause(self, bool):
        self.__pause = bool
    
    def update(self, draw = True):
        super(GamePanel, self).update()
        state = self.judge(self.__lvlmap)
        if state == GamePanel.WIN:
            self.con.unregister(self)
            self.con.register(ScorePanel(self.con, self.sub, self.__level, self.__lvlmap, True))
        elif state == GamePanel.LOSE:
            self.con.unregister(self)
            self.con.register(ScorePanel(self.con, self.sub, self.__level, self.__lvlmap, False))
        elif state == GamePanel.GAMING:
            # sprite update
            if not self.__pause:
                for spr in self.__lvlmap.updates:
                    spr.update()
            # draw window
            self.sub.fill((0, 0, 0))
            self.__lvlmap.render.draw(self.sub)
            self.__state.update()
        
    def judge(self, lvlmap): # game judge
        if not lvlmap.sprgroup.sprites((EnemyFactory, Enemy, StarEffect, BossEnemy, BossFactory)):
            return GamePanel.WIN
        else:
            alive = False # all players dead
            for spr in lvlmap.sprgroup.sprites((Player, Plane)):
                if spr.vtl_alive(): alive = True
            for base in lvlmap.sprgroup.sprites((Base, )):
                if not base.vtl_alive(): alive = False
            if not alive: return GamePanel.LOSE
        return GamePanel.GAMING
    
class StatePanel(Panel):
    """ game state panel """
    GRID = 20
    COLOR = (200, 200, 100)
    SPLIT = (100, 100, 200)
    def __init__(self, con, sub, level, datamap):
        super(StatePanel, self).__init__(con, sub, grid = StatePanel.GRID)
        self.__level = level
        self.__datamap = datamap
        self.__font1 = pygame.font.Font('res\\impact.ttf', 15)
        self.__font2 = pygame.font.Font('res\\impact.ttf', 20)
        self.__font3 = pygame.font.Font('res\\impact.ttf', 30)
        self.__player = load_frame('res\\tank.bmp', (8, 8), (0, 0, 0))[0]
        blend_color(self.__player, (255, 255, 100))
        self.__plane = load_frame('res\\plane.bmp', (4, 1), (0, 0, 0))[0]
        self.__enemy = load_frame('res\\tank.bmp', (8, 8), (0, 0, 0))[32]
        blend_color(self.__enemy, (100, 255, 255))
        self.__boss = load_frame('res\\tank.bmp', (8, 8), (0, 0, 0))[56]
        blend_color(self.__boss, (250, 100, 100))
        
    def update(self):
        super(StatePanel, self).update()
        self.sub.fill((21, 55, 77))
        player, plane, enemy, alive, boss = self.__datamap.get_state()
        # split line
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 0), (1, 1), (2, 2)), 
                         self.center_node((7, 0), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 3), (1, 1), (2, 2)), 
                         self.center_node((7, 3), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 18), (1, 1), (2, 2)), 
                         self.center_node((7, 18), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 23), (1, 1), (2, 2)), 
                         self.center_node((7, 23), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 27), (1, 1), (2, 2)), 
                         self.center_node((7, 27), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, StatePanel.SPLIT, 
                         self.center_node((0, 31), (1, 1), (2, 2)), 
                         self.center_node((7, 31), (1, 1), (2, 2)), 2)
        pygame.draw.line(self.sub, (100, 100, 100), 
                         self.center_node((0, 8), (1, 2), (1, 1)), 
                         self.center_node((7, 8), (1, 2), (1, 1)))
        pygame.draw.line(self.sub, (100, 100, 100), 
                         self.center_node((0, 11), (1, 2), (1, 1)), 
                         self.center_node((7, 11), (1, 2), (1, 1)))
        pygame.draw.line(self.sub, (100, 100, 100), 
                         self.center_node((0, 14), (1, 2), (1, 1)), 
                         self.center_node((7, 14), (1, 2), (1, 1)))
        # string
        str = self.__font3.render('Level  %d' % (self.__level, ), 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((1, 1), (6, 2), str.get_rect().size))
        # enemy
        self.sub.blit(self.__enemy, self.center_node((1, 4), (2, 2), self.__enemy.get_rect().size))
        count = 0
        for i in xrange(0, len(enemy)):
            str = self.__font2.render('F%d' % (i, ), 1, StatePanel.COLOR)
            self.sub.blit(str, self.center_node((0, 6 + 3 * i), (2, 3), str.get_rect().size))
            for j in xrange(0, len(enemy[i])):
                count += enemy[i][j][0]
                str = self.__font1.render('L%d:%d' % (enemy[i][j][1], enemy[i][j][0]), 1, StatePanel.COLOR)
                x = 2 + (j % 3) * 2
                y = 6 + 3 * i + j // 3
                self.sub.blit(str, self.center_node((x, y), (2, 1), str.get_rect().size))
        str = self.__font1.render('Total', 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((3, 4), (2, 1), str.get_rect().size))
        str = self.__font1.render('%d' % (count, ), 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((3, 5), (2, 1), str.get_rect().size))
        str = self.__font1.render('Alive', 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((5, 4), (2, 1), str.get_rect().size))
        str = self.__font1.render('%d' % (alive, ), 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((5, 5), (2, 1), str.get_rect().size))
        # boss
        self.sub.blit(self.__boss, self.center_node((3, 19), (2, 2), self.__boss.get_rect().size))
        str = self.__font1.render('Time', 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((1, 21), (3, 1), str.get_rect().size))
        str = self.__font1.render('HP', 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((1, 22), (3, 1), str.get_rect().size))
        str = self.__font1.render('%d' % (boss[0], ), 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((4, 21), (3, 1), str.get_rect().size))
        str = self.__font1.render('%d' % (boss[1], ), 1, StatePanel.COLOR)
        self.sub.blit(str, self.center_node((4, 22), (3, 1), str.get_rect().size))        
        # player & plane
        playerhp = self.__font1.render('HP  %d' % (player[0], ), 1, StatePanel.COLOR)
        playerpower = self.__font1.render('POWER  %d' % (player[1], ), 1, StatePanel.COLOR)
        playerkill = self.__font1.render('KILL  %d' % (player[2], ), 1, StatePanel.COLOR)
        planehp = self.__font1.render('HP  %d' % (plane[0], ), 1, StatePanel.COLOR)
        planepower = self.__font1.render('POWER  %d' % (plane[1], ), 1, StatePanel.COLOR)
        planekill = self.__font1.render('KILL  %d' % (plane[2], ), 1, StatePanel.COLOR)
        self.sub.blit(self.__player, self.center_node((1, 24), (3, 3), self.__player.get_rect().size))
        self.sub.blit(playerhp, self.center_node((4, 24), (3, 1), playerhp.get_rect().size))
        self.sub.blit(playerpower, self.center_node((4, 25), (3, 1), playerpower.get_rect().size))
        self.sub.blit(playerkill, self.center_node((4, 26), (3, 1), playerkill.get_rect().size))
        self.sub.blit(self.__plane, self.center_node((1, 28), (3, 3), self.__plane.get_rect().size))
        self.sub.blit(planehp, self.center_node((4, 28), (3, 1), planehp.get_rect().size))
        self.sub.blit(planepower, self.center_node((4, 29), (3, 1), planepower.get_rect().size))
        self.sub.blit(planekill, self.center_node((4, 30), (3, 1), planekill.get_rect().size))
        
class ScorePanel(Panel):
    """ game score panel """
    KEYS = (K_RETURN, )
    LVLSIZE = (240, 240)
    COLOR = (100, 200, 200)
    GRID = 40
    STEP = 10 # grid step
    CG = (# (dx, dy, da, step)
          (0, 30, 255 / 10, 10), # mini level
          (0, 0, 255 / 10, 10), # level lab
          (-5, 0, 255 / 25, 25), # left list1
          (-5, 0, 255 / 25, 25), # left list2
          (5, 0, 255 / 25, 25), # right list1
          (5, 0, 255 / 25, 25), # right list2
          (0, -30, 255 / 10, 10), # title
          )
    def __init__(self, con, sub, level, datamap, win = True):
        super(ScorePanel, self).__init__(con, sub, focus = True, grid = ScorePanel.GRID)
        self.regsiter_key(ScorePanel.KEYS, self.pros_keys)
        self.__level = level
        self.__am = [[], [], [], [], [], [], []] # mini, lab, left1, left2, right1, right2, title
        self.__tick = 0
        self.__state = 1
        self.__win = win
        self.__init_score(datamap)
        
    def __init_score(self, datamap):
        font1 = pygame.font.Font('res\\comic.ttf', 25)
        font2 = pygame.font.Font('res\\comic.ttf', 50)
        font2.set_bold(1)
        font3 = pygame.font.Font('res\\comic.ttf', 80)
        font3.set_bold(1)
        re = pygame.Surface((1, 1))
        re.fill((21, 55, 77))
        player, plane, enemy, alive, boss = datamap.get_state()
        # title
        if self.__win: 
            str = 'Win'
            playsound('win')
        else: 
            str = 'Lose' 
            playsound('lose')
        img = font3.render('Battle %s !' % (str, ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[6].append((img, self.center_node((6, 2), (8, 2), img.get_rect().size)))        
        # level
        img = mini_level(self.__level, ScorePanel.LVLSIZE)
        self.__am[0].append((img, self.center_node((7, 8), (6, 6), img.get_rect().size)))
        img = font2.render('Level  %d' % (self.__level, ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[1].append((img, self.center_node((7, 5), (6, 2), img.get_rect().size)))
        # player
        self.__am[2].append((re, (0, 0))) # averge time
        img = load_frame('res\\tank.bmp', (8, 8), (0, 0, 0))[0]
        blend_color(img, (255, 255, 100))
        self.__am[2].append((img, self.center_node((1, 6), (1, 1), img.get_rect().size)))
        self.__am[2].append((re, (0, 0))) # averge time
        img = font1.render('HP  %d' % (player[0], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[4].append((img, self.center_node((3, 5), (3, 1), img.get_rect().size)))
        img = font1.render('POWER  %d' % (player[1], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[4].append((img, self.center_node((3, 6), (3, 1), img.get_rect().size)))
        img = font1.render('KILL  %d' % (player[2], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[4].append((img, self.center_node((3, 7), (3, 1), img.get_rect().size)))
        for i in xrange(0, 8):
            if player[3].has_key(i):
                img = font1.render('L%d   %d' % (i, len(player[3][i])), 1, ScorePanel.COLOR, (0, 0, 0))
                img.set_colorkey((0, 0, 0))
            else:
                img = font1.render('L%d   0' % (i, ), 1, ScorePanel.COLOR, (0, 0, 0))
                img.set_colorkey((0, 0, 0))
            if i % 2 == 0:
                self.__am[2].append((img, self.center_node((1, 9 + i // 2), (2, 1), img.get_rect().size)))
            else:
                self.__am[4].append((img, self.center_node((4, 9 + i // 2), (2, 1), img.get_rect().size)))
        # plane
        self.__am[3].append((re, (0, 0))) # averge time
        img = load_frame('res\\plane.bmp', (4, 1), (0, 0, 0))[0]
        self.__am[3].append((img, self.center_node((14, 6), (1, 1), img.get_rect().size)))
        self.__am[3].append((re, (0, 0))) # averge time
        img = font1.render('HP  %d' % (plane[0], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[5].append((img, self.center_node((16, 5), (3, 1), img.get_rect().size)))
        img = font1.render('POWER  %d' % (plane[1], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[5].append((img, self.center_node((16, 6), (3, 1), img.get_rect().size)))
        img = font1.render('KILL  %d' % (plane[2], ), 1, ScorePanel.COLOR, (0, 0, 0))
        img.set_colorkey((0, 0, 0))
        self.__am[5].append((img, self.center_node((16, 7), (3, 1), img.get_rect().size)))
        for i in xrange(0, 8):
            if plane[3].has_key(i):
                img = font1.render('L%d   %d' % (i, len(plane[3][i])), 1, ScorePanel.COLOR, (0, 0, 0))
                img.set_colorkey((0, 0, 0))
            else:
                img = font1.render('L%d   0' % (i, ), 1, ScorePanel.COLOR, (0, 0, 0))
                img.set_colorkey((0, 0, 0))
            if i % 2 == 0:
                self.__am[3].append((img, self.center_node((14, 9 + i // 2), (2, 1), img.get_rect().size)))
            else:
                self.__am[5].append((img, self.center_node((17, 9 + i // 2), (2, 1), img.get_rect().size)))
        # boss 
        img = load_frame('res\\tank.bmp', (8, 8), (0, 0, 0))[56]
        blend_color(img, (250, 100, 100))
        if player[4]:
            self.__am[2].append((img, self.center_node((1, 13), (1, 1), img.get_rect().size)))
        elif plane[4]:
            self.__am[5].append((img, self.center_node((17, 13), (1, 1), img.get_rect().size)))
        
    def pros_keys(self, keyname):
        if self.__state == 0: self.__state = -1
        
    def __action(self, tick):
        action = False
        for i in xrange(0, len(self.__am)): # action list
            for j in xrange(0, len(self.__am[i])): # list item
                step = tick - j * ScorePanel.STEP
                cgstep = ScorePanel.CG[i][3] - step
                if step >= 0:
                    if cgstep >= 0:
                        action = True
                        img = self.__am[i][j][0].copy()
                        img.set_alpha(ScorePanel.CG[i][2] * step)
                        pos = (self.__am[i][j][1][0] + ScorePanel.CG[i][0] * cgstep, 
                               self.__am[i][j][1][1] + ScorePanel.CG[i][1] * cgstep)
                        self.sub.blit(img, pos)
                    else:
                        self.sub.blit(self.__am[i][j][0], self.__am[i][j][1])
        return action
        
    def update(self):
        super(ScorePanel, self).update()
        self.sub.fill((21, 55, 77))
        # string
        if self.__state == 1:
            if self.__action(self.__tick): self.__tick += 1
            else: self.__state = 0
        elif self.__state == 0:
            self.__action(self.__tick)
        elif self.__state == -1:
            self.__tick -= 1 # to avert tick > max
            if not self.__action(self.__tick): 
                self.con.unregister(self)
                if self.__win:
                    self.con.level_begin((self.__level + 1) % len(mapcfg))
                else:
                    self.con.start_menu()

class LevelPanel(Panel):
    """ level change panel """
    KEYS = (K_RETURN, )
    LVLSIZE = (300, 300)
    STEP = 10
    OFFSET = (60, 0)
    ALPHA = 255 / 10
    def __init__(self, con, sub, level):
        super(LevelPanel, self).__init__(con, sub, focus = True)
        self.regsiter_key(LevelPanel.KEYS, self.pros_keys)
        self.level = level
        self.lvlimg = mini_level(level, LevelPanel.LVLSIZE)
        self.lvlstr = self.__level_str(level)
        re = self.sub.get_rect()
        self.imgpos = ((re.width - self.lvlimg.get_rect().width) // 2, 100)
        self.strpos = ((re.width - self.lvlstr.get_rect().width) // 2, 450)
        self.__tick = 0
        self.__state = 2 # 1: come, 0: wait, -1: go
        playsound('level')
    
    def __level_str(self, level):
        font = pygame.font.Font('res\\comic.ttf', 75)
        str = font.render('Level %d' % (level, ), 1, (100, 200, 100), (0, 0, 0))
        str.set_colorkey((0, 0, 0))
        return str        
    
    def pros_keys(self, keyname):
        if self.__state == 0: self.__state = -1
        
    def update(self):
        super(LevelPanel, self).update()
        self.sub.fill((21, 55, 77))
        img = self.lvlimg.copy()
        imgpos = (self.imgpos[0], self.imgpos[1])
        str = self.lvlstr.copy()
        strpos = (self.strpos[0], self.strpos[1])
        if self.__state == 2:
            img.set_alpha(LevelPanel.ALPHA * self.__tick)
            imgpos = (imgpos[0] + LevelPanel.OFFSET[0] * (LevelPanel.STEP - self.__tick), 
                      imgpos[1] + LevelPanel.OFFSET[1] * (LevelPanel.STEP - self.__tick))
            self.__tick += 1
            if self.__tick > LevelPanel.STEP:
                self.__tick = 0
                self.__state = 1
            self.sub.blit(img, imgpos)
        elif self.__state == 1:
            str.set_alpha(LevelPanel.ALPHA * self.__tick)
            strpos = (strpos[0] + LevelPanel.OFFSET[0] * (LevelPanel.STEP - self.__tick), 
                      strpos[1] + LevelPanel.OFFSET[1] * (LevelPanel.STEP - self.__tick))
            self.__tick += 1
            if self.__tick > LevelPanel.STEP:
                self.__tick = LevelPanel.STEP
                self.__state = 0
            self.sub.blit(img, imgpos)
            self.sub.blit(str, strpos)
        elif self.__state == 0:
            self.sub.blit(img, imgpos)
            self.sub.blit(str, strpos)
        elif self.__state == -1:
            str.set_alpha(LevelPanel.ALPHA * self.__tick)
            strpos = (strpos[0] - LevelPanel.OFFSET[0] * (LevelPanel.STEP - self.__tick), 
                      strpos[1] - LevelPanel.OFFSET[1] * (LevelPanel.STEP - self.__tick))
            self.__tick -= 1  
            if self.__tick < 0:
                self.__tick = LevelPanel.STEP
                self.__state = -2
            self.sub.blit(img, imgpos)
            self.sub.blit(str, strpos)
        elif self.__state == -2:
            img.set_alpha(LevelPanel.ALPHA * self.__tick)
            imgpos = (imgpos[0] - LevelPanel.OFFSET[0] * (LevelPanel.STEP - self.__tick), 
                      imgpos[1] - LevelPanel.OFFSET[1] * (LevelPanel.STEP - self.__tick))
            self.__tick -= 1  
            if self.__tick < 0:
                self.con.unregister(self)
                self.con.level_start(self.level)
            self.sub.blit(img, imgpos)
        









